package ru.ifmo.thesis.suite;

import java.io.FileInputStream;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import ru.ifmo.thesis.domain.AbstractGeoService;
import ru.ifmo.thesis.domain.GeoServiceFactory;
import ru.ifmo.thesis.domain.Job;
import ru.ifmo.thesis.domain.Location;
import ru.ifmo.thesis.domain.Shedule;
import ru.ifmo.thesis.domain.Worker;
import ru.ifmo.thesis.quantities.Minutes;
import ru.ifmo.thesis.solver.DirectSolver;
import ru.ifmo.thesis.task.Task;
import ru.ifmo.thesis.util.Algorithms;
import ru.ifmo.thesis.util.Loggers;

public class GreedySolver {
	
	private static class MicroShedile {
		private final Worker worker;
		private final List<Job> jobList;
		
		public MicroShedile(Worker worker) {
			this.worker = worker;
			this.jobList = new ArrayList<Job>();
		}
		
		public Minutes totalTime() {
			double result = 0;
			AbstractGeoService geoService = GeoServiceFactory.getGeoService();
			Location prev = geoService.getOfficeLocation();
			for (Job j : jobList) {
				result += geoService.distance(prev, j.getLocation()).value() / 60;
				result += j.getExpectedEliminationTime().value();
				prev = j.getLocation();
			}
			return new Minutes(result);
		}
		
		public List<Job> getJobList() {
			return jobList;
		}
		
		public Worker getWorker() {
			return worker;
		}
	}

	public static void main(String[] args) throws Exception {
        Loggers.initLoggerFromConfig();
		
		String suiteFileName = args[0];
		
		String outputFileName = args[1];
		
		StringBuilder output = new StringBuilder();
		
		TaskSuite taskSuite = SuiteSerializer.readZippedSuite(
				new FileInputStream(suiteFileName));

		for (Task t : taskSuite.getTasks()) {
			Shedule s = solve(t);
			output.append(Algorithms.calculateSheduleCost(s) + "\n");
		}
		
		FileWriter writer = new FileWriter(outputFileName);
		writer.write(output.toString());
		writer.close();
	}

	private static Shedule solve(Task t) {
		//Sort tasks according to penalty!
		List<Job> jobs = new LinkedList<Job>(t.getJobs());
		
		List<MicroShedile> shedules = new ArrayList<MicroShedile>();
		
		for (Worker w : t.getWorkers()) {
			MicroShedile e = new MicroShedile(w);
			e.getJobList().add(jobs.remove(0));
			shedules.add(e);
		}
		
		while (!jobs.isEmpty()) {
			Collections.sort(shedules, new Comparator<MicroShedile>() {
				@Override
				public int compare(MicroShedile o1, MicroShedile o2) {
					return o1.totalTime().value().compareTo(o2.totalTime().value());
				}
			});
			
			final Minutes bestTime = shedules.get(0).totalTime();
			Collections.sort(jobs, new Comparator<Job>() {
				@Override
				public int compare(Job o1, Job o2) {
					Minutes first = new Minutes(bestTime.value() + o1.getExpectedEliminationTime().value());
					Minutes second = new Minutes(bestTime.value() + o2.getExpectedEliminationTime().value());
					
					return (int) (-o1.getContractOptions().getPenaltyFunction().
							calculatePenalty(first) + o2.getContractOptions().getPenaltyFunction()
							.calculatePenalty(second));
				}
			});
			
			Job current = jobs.remove(0);
			
			
			
			shedules.get(0).getJobList().add(current);
		}
		
		return constructShedule(shedules);
	}

	private static Shedule constructShedule(List<MicroShedile> shedules) {
		Shedule result = new Shedule();		
		for (MicroShedile ms : shedules) {
			for (Job j : ms.getJobList()) {
				result.appendJob(ms.getWorker(), j);
			}
		}
		return result;
	}
}
